package storage

import (
	"context"
	"crypto/md5"
	"crypto/sha1"
	"encoding/hex"
	"fmt"
	"io"
	"mime/multipart"
	"os"
	"path/filepath"
	"strings"
	"time"

	"go-caipu/pkg/services/storage/models"
)

// LocalStorage 本地存储实现
type LocalStorage struct {
	config   models.StorageConfig
	basePath string
}

func init() {
	Register(models.StorageLocal, NewLocalStorage)
}

// NewLocalStorage 创建本地存储实例
func NewLocalStorage() Storage {
	return &LocalStorage{}
}

// Init 初始化本地存储
func (l *LocalStorage) Init(config models.StorageConfig) error {
	l.config = config

	// 本地存储的basePath可以从endpoint获取
	l.basePath = config.BucketName
	if l.basePath == "" {
		return fmt.Errorf("local storage endpoint (base path) is required")
	}

	// 确保目录存在
	if err := os.MkdirAll(l.basePath, 0755); err != nil {
		return fmt.Errorf("failed to create local storage directory: %v", err)
	}

	return nil
}
func (l *LocalStorage) Upload(ctx context.Context, reader io.Reader, storage *models.FileStorage) (*models.FileStorage, error) {
	// 创建目标文件路径
	fullPath := l.getFullPath(storage.StoragePath, storage.FileKey)
	if err := os.MkdirAll(filepath.Dir(fullPath), 0755); err != nil {
		return nil, fmt.Errorf("failed to create directory: %v", err)
	}
	// 创建目标文件
	dst, err := os.Create(fullPath)
	if err != nil {
		return nil, fmt.Errorf("failed to create destination file: %v", err)
	}
	defer dst.Close() // 创建目标文件

	// 复制文件内容，同时计算哈希
	if _, err := io.Copy(dst, reader); err != nil {
		// 如果出错，删除可能已创建的文件
		os.Remove(fullPath)
		return nil, fmt.Errorf("failed to copy file content: %v", err)
	}
	storage.AccessURL = storage.AccessURL
	// 创建并返回FileStorage记录
	return storage, nil
}

// 标记 要删除
func (l *LocalStorage) UploadFile(ctx context.Context, file *multipart.FileHeader, storagePath, fileKey string) (*models.FileStorage, error) {

	// 打开上传的文件
	src, err := file.Open()
	if err != nil {
		return nil, fmt.Errorf("failed to open uploaded file: %v", err)
	}
	defer src.Close()

	// 计算文件的MD5和SHA1
	md5Hash := md5.New()
	sha1Hash := sha1.New()
	multiWriter := io.MultiWriter(md5Hash, sha1Hash)

	// 创建目标文件路径
	fullPath := l.getFullPath(storagePath, fileKey)
	if err := os.MkdirAll(filepath.Dir(fullPath), 0755); err != nil {
		return nil, fmt.Errorf("failed to create directory: %v", err)
	}

	// 创建目标文件
	dst, err := os.Create(fullPath)
	if err != nil {
		return nil, fmt.Errorf("failed to create destination file: %v", err)
	}
	defer dst.Close()

	// 复制文件内容，同时计算哈希
	if _, err := io.Copy(multiWriter, src); err != nil {
		return nil, fmt.Errorf("failed to copy file content: %v", err)
	}

	// 重置文件指针，以便重新读取
	if _, err := src.Seek(0, io.SeekStart); err != nil {
		return nil, fmt.Errorf("failed to seek file: %v", err)
	}

	// 再次复制到目标文件
	if _, err := io.Copy(dst, src); err != nil {
		return nil, fmt.Errorf("failed to write file content: %v", err)
	}

	// 提取文件扩展名
	fileExt := filepath.Ext(file.Filename)
	if fileExt != "" {
		fileExt = fileExt[1:] // 移除点号
	}

	// 创建并返回FileStorage记录
	return &models.FileStorage{
		FileName:    file.Filename,
		FileKey:     fileKey,
		FileSize:    file.Size,
		FileType:    file.Header.Get("Content-Type"),
		FileExt:     fileExt,
		StorageType: models.StorageLocal,
		// BucketName:  cmd.BucketName,
		StoragePath: storagePath,
		AccessURL:   storagePath,
		MD5:         hex.EncodeToString(md5Hash.Sum(nil)),
		SHA1:        hex.EncodeToString(sha1Hash.Sum(nil)),
		Status:      1,
		CreatedAt:   time.Now(),
		UpdatedAt:   time.Now(),
	}, nil
}

// Download 获取文件
func (l *LocalStorage) Download(ctx context.Context, storagePath, fileKey string) (io.ReadCloser, error) {
	fullPath := l.getFullPath(storagePath, fileKey)
	file, err := os.Open(fullPath)
	if err != nil {
		return nil, fmt.Errorf("failed to open file: %v", err)
	}
	return file, nil
}

// Delete 删除文件
func (l *LocalStorage) Delete(ctx context.Context, storagePath, fileKey string) error {
	fullPath := l.getFullPath(storagePath, fileKey)
	if err := os.Remove(fullPath); err != nil && !os.IsNotExist(err) {
		return fmt.Errorf("failed to delete file: %v", err)
	}
	return nil
}

// GetFileURL 获取文件访问URL
func (l *LocalStorage) GetFileURL(ctx context.Context, staticPath, fileKey string) (string, error) {
	return l.getAccessURL(staticPath, "", fileKey), nil
}

// getFullPath 构建完整文件路径
func (l *LocalStorage) getFullPath(storagePath, fileKey string) string {
	parts := []string{l.basePath}
	if storagePath != "" {
		parts = append(parts, storagePath)
	}
	parts = append(parts, fileKey)
	return filepath.Join(parts...)
}

// getAccessURL 构建访问URL
func (l *LocalStorage) getAccessURL(bucketName, storagePath, fileKey string) string {
	baseURL := l.config.Domain
	if baseURL == "" {
		baseURL = "/"
	}

	parts := []string{}
	if bucketName != "" {
		parts = append(parts, bucketName)
	}
	if storagePath != "" {
		parts = append(parts, storagePath)
	}
	parts = append(parts, fileKey)

	return fmt.Sprintf("%s/%s", strings.TrimSuffix(baseURL, "/"), strings.Join(parts, "/"))
}
